package org.zsk.cfl.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.zsk.cfl.exception.BizException;
import org.zsk.cfl.service.IFlowService;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * </p>
 *
 * @author zsk
 * @date 2020/10/20 上午10:32
 */
@Service
@Slf4j
public class IFlowServiceImpl implements IFlowService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private TaskService taskService;

    @Qualifier("processEngine")
    @Autowired
    private ProcessEngine processEngine;

    @Override
    public Map<String, Object> createFlow(String filePath, String deploymentName) {
        //解析BPMN模型看是否成功
        Map<String, Object> res = new HashMap<>(2);
        BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        try (InputStream inputStream = new FileInputStream(new File(filePath));
             InputStream deploy = new FileInputStream(new File(filePath))) {
            BpmnModel model = bpmnXMLConverter.convertToBpmnModel(factory.createXMLStreamReader(inputStream));
            List<Process> processes = model.getProcesses();
            Process curProcess;
            if (CollectionUtils.isEmpty(processes)) {
                log.error("BPMN模型没有配置流程");
                return null;
            }
            res.put("processes", processes);
            curProcess = processes.get(0);

            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                    .name(deploymentName)
                    .addInputStream(filePath, deploy);

            Deployment deployment = deploymentBuilder.deploy();
            res.put("deployment", deployment);
            log.warn("部署流程 name:{} key:{} deploy{}", curProcess.getName(), deployment.getKey(), deployment);
            return res;
        } catch (Exception e) {
            log.error("BPMN模型创建流程异常", e);
            return null;
        }
    }


    @Override
    public ProcessInstance startFlow(String processId, Map<String, Object> paras) {
        if (StringUtils.isEmpty(processId)) {
            return null;
        }

        return runtimeService.startProcessInstanceById(processId, paras);
    }

    @Override
    public boolean isFinished(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery()
                .finished()
                .processInstanceId(processInstanceId)
                .count() > 0;
    }

    @Override
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) {
        //获得当前活动的节点
        String processDefinitionId = "";
        // 如果流程已经结束，则得到结束节点
        if (this.isFinished(processId)) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(processId)
                    .singleResult();

            processDefinitionId = pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processId)
                    .singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }

        //获得活动的节点
        Optional<List<HistoricActivityInstance>> highLightedActivityListOpt = Optional.ofNullable(
                historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(processId)
                        .orderByHistoricActivityInstanceStartTime()
                        .asc()
                        .list()
        );
        List<String> highLightedActivities = highLightedActivityListOpt
                .orElseThrow(() -> new BizException("活动的节点不存在"))
                .stream()
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList()
                );

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        List<String> flows = new ArrayList<>();

        try (InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp",
                highLightedActivities, flows, processEngineConfiguration.getActivityFontName(),
                processEngineConfiguration.getLabelFontName(), processEngineConfiguration.getAnnotationFontName(),
                ProcessEngineConfiguration.class.getClassLoader(), 1.0, true);
             BufferedInputStream bufferedInputStream = new BufferedInputStream(in);
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(httpServletResponse.getOutputStream())
        ) {
            int legth;
            byte[] buf = new byte[1024];
            while ((legth = bufferedInputStream.read(buf)) != -1) {
                bufferedOutputStream.write(buf, 0, legth);
            }
        } catch (IOException e) {
            log.error("操作异常", e);
        }

    }

    /**
     * 完成任务
     */
    @Override
    public void completeTask(String taskId, Map<String, Object> paras) {
        Optional.ofNullable(taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult())
                .orElseThrow(() -> new BizException("任务不存在"));

        if (CollectionUtils.isEmpty(paras)) {
            taskService.complete(taskId);
        } else {
            taskService.complete(taskId, paras);
        }
    }

}
